# @Time : 2021/4/8 17:57
# @Author : Fioman 
# @Phone : 13149920693
import threading

import gevent

from db_tools.db_handler import DbHandler
from tools.hd_logger import HdLogger
import settings
from tools.send_to_ui_tools import *
from tools.config_params import ConfigParams

"""
设备状态监控,每个设备创建一个线程,用来控制设备的状态,检测到设备状态以后要做的事情.
"""
cp = ConfigParams()


class StatesListener(object):
    def __init__(self, leftCam, rightCam, cutterCam1, cutterCam2, offlineZmc, camZmc, cbjZmc, cutterZmc, deviceStates):
        self.db = DbHandler()
        self.logger = HdLogger()
        self.states = deviceStates
        self.offlineZmc = offlineZmc
        self.camZmc = camZmc
        self.cbjZmc = cbjZmc
        self.cutterZmc = cutterZmc
        self.leftCam = leftCam
        self.rightCam = rightCam
        self.cutterCam1 = cutterCam1
        self.cutterCam2 = cutterCam2
        self.loopDuration = 0.5
        self.managerThread = threading.Thread(target=self.manager, name="managerThread")
        self.offlineZmcListenerThread = threading.Thread(target=self.offline_zmc_listener, name="offlineZmcListenerThread")
        self.camZmcListenerThread = threading.Thread(target=self.cam_zmc_listener, name="camZmcListenerThread")
        self.cbjZmcListenerThread = threading.Thread(target=self.cbj_zmc_listener, name="cbjZmcListenerThread")
        self.cutterZmcListenerThread = threading.Thread(target=self.cutter_zmc_listener, name="cutterZmcListenerThread")
        self.cameraGroupListenerThread = threading.Thread(target=self.camera_group_listener, name="cameraGroupListenerThread")

    def offline_zmc_listener(self):
        """
        离线上料控制器的监听函数
        :return:
        """
        while settings.IS_MAIN_ALIVE:
            connectState = self.offlineZmc.check_connect()
            if connectState:
                # 检测所有的报警信息
                self.states.shieldStart = self.offlineZmc.get_shielding_start()
                ret, val = self.offlineZmc.get_modbus_bit(15, 1)
                self.states.offlineOnlineStart = True if val[0] == 1 else False
                val = [self.offlineZmc.get_modbus_bit(bitNum, 1)[1][0] for bitNum in range(500, 550)]
                alarmInfo = self.get_offline_alarm_info_by_bit_val(val)
                self.states.offlineAlarmInfoVisible = False if alarmInfo == "" else True
                self.states.offlineAlarmInfo = alarmInfo

                # 离线上料的其他的状态也搞进来,主要是丢放板的按钮的切换加上去,
                ret, isThrowBoard = self.offlineZmc.is_throw_board()
                if ret != 0:
                    errorInfo = isThrowBoard
                    print(errorInfo)
                    self.logger.debug(errorInfo)
                else:
                    self.states.offlineZmcStates.isThrowBoard = isThrowBoard

                ret, val = self.offlineZmc.is_rs_stop()
                if ret == 0:
                    if val == 1:
                        self.states.offlineZmcStates.rs = True
                        if not self.states.offlineZmcIsRs:
                            self.states.offlineZmcIsRs = True
                            self.logger.debug("检测到离线上料控制器进入到急停状态")
                    else:
                        self.states.offlineZmcStates.rs = False
                        if self.states.offlineZmcIsRs:
                            self.states.offlineZmcIsRs = False
                            self.logger.debug("检测到离线上料控制器急停状态去除!")

                ret, val = self.offlineZmc.is_running()
                if ret == 0:
                    if val == 1:
                        self.states.offlineZmcStates.run = True
                        if not self.states.offlineZmcIsRun:
                            self.states.offlineZmcIsRun = True
                            self.logger.debug("检测到离线上料进入运行状态")
                    else:
                        self.states.offlineZmcStates.run = False
                        if self.states.offlineZmcIsRun:
                            self.states.offlineZmcIsRun = False
                            self.logger.debug("检测到离线上料进入停止状态")

            else:
                connectRes = self.offlineZmc.reconnect_offline_zmc()
                if connectRes == "ok":
                    self.states.offlineZmcStates.online = True
                    self.states.offlineZmcStates.rs = False
                    self.states.offlineZmcStates.run = False
                else:
                    self.states.offlineZmcStates.online = False
                    self.states.offlineZmcStates.rs = False
                    self.states.offlineZmcStates.run = False
                    gevent.sleep(1)
            gevent.sleep(self.loopDuration)

    def cam_zmc_listener(self):
        """
        上料控制器的监听线程类
        :return:
        """
        while settings.IS_MAIN_ALIVE:

            connectState = self.camZmc.check_connect()
            if connectState:
                # 检测设备是否是急停状态
                ret, val = self.camZmc.is_rs_stop()
                if ret == 0:
                    if val == 1:
                        self.states.camZmcStates.rs = True
                        if not self.states.camZmcIsRs:
                            self.states.camZmcIsRs = True  # 进入急停状态,不会再重复执行此段代码
                            self.logger.debug("检测到上料急停")
                    else:
                        self.states.camZmcStates.rs = False
                        if self.states.camZmcIsRs:
                            self.states.camZmcIsRs = False
                            self.logger.debug("检测到上料急停状态去除")

                ret, val = self.camZmc.is_running()
                if ret == 0:
                    if val == 1:
                        self.states.camZmcStates.run = True
                        if not self.states.camZmcIsRun:
                            self.states.camZmcIsRun = True
                            self.logger.debug("检测到上料进入运行状态")
                    else:
                        self.states.camZmcStates.run = False
                        if self.states.camZmcIsRun:
                            self.states.camZmcIsRun = False
                            self.logger.debug("检测到上料进入停止状态")
            else:
                connectRes = self.camZmc.reconnect_cam_zmc()
                if connectRes == "ok":
                    self.states.camZmcStates.online = True
                    self.states.camZmcStates.rs = False
                    self.states.camZmcStates.run = False
                else:
                    self.states.camZmcStates.online = False
                    self.states.camZmcStates.rs = False
                    self.states.camZmcStates.run = False
                gevent.sleep(self.loopDuration)

            gevent.sleep(self.loopDuration)

    def cbj_zmc_listener(self):
        """
        裁切机控制器的监听线程类
        :return:
        """
        while settings.IS_MAIN_ALIVE:

            connectState = self.cbjZmc.check_connect()
            if connectState:
                # 检测设备是否是急停状态
                ret, val = self.cbjZmc.is_rs_stop()
                if ret == 0:
                    if val == 1:
                        self.states.cbjZmcStates.rs = True
                        if not self.states.cbjZmcIsRs:
                            self.states.cbjZmcIsRs = True  # 进入急停状态,不会再重复执行此段代码
                            self.logger.debug("检测到裁切机急停")
                    else:
                        self.states.cbjZmcStates.rs = False
                        if self.states.cbjZmcIsRs:
                            self.logger.debug("裁切机急停状态去除!")
                            self.states.cbjZmcIsRs = False

                ret, val = self.cbjZmc.is_running()
                if ret == 0:
                    if val == 1:
                        self.states.cbjZmcStates.run = True
                        if not self.states.cbjZmcIsRun:
                            self.states.cbjZmcIsRun = True
                            self.logger.debug("检测到裁切机进入运行状态")
                    else:
                        self.states.cbjZmcStates.run = False
                        if self.states.cbjZmcIsRun:
                            self.states.cbjZmcIsRun = False
                            self.logger.debug("检测到裁切机进入停止状态")
            else:
                connectRes = self.cbjZmc.reconnect_cbj_zmc()
                if connectRes == "ok":
                    self.states.cbjZmcStates.online = True
                    self.states.cbjZmcStates.rs = False
                    self.states.cbjZmcStates.run = False
                else:
                    self.states.cbjZmcStates.online = False
                    self.states.cbjZmcStates.rs = False
                    self.states.cbjZmcStates.run = False

            gevent.sleep(self.loopDuration)

    def cutter_zmc_listener(self):
        """
        铡刀控制器的监听线程类
        :return:
        """
        while settings.IS_MAIN_ALIVE:
            connectState = self.cutterZmc.check_connect()
            if connectState:
                # 检测所有的报警信息
                val = [self.cutterZmc.get_modbus_bit(bitNum, 1)[1][0] for bitNum in range(500, 650)]
                alarmInfo = self.get_main_alarm_info_by_bit_val(val)
                self.states.mainAlarmInfo = alarmInfo
                self.states.mainAlarmInfoVisible = True if alarmInfo != "" else False
                ret, val = self.cutterZmc.is_rs_stop()
                if ret == 0:
                    if val == 1:
                        self.states.cutterZmcStates.rs = True
                        if not self.states.cutterZmcIsRs:
                            self.states.cutterZmcIsRs = True
                            self.logger.debug("检测到铡刀控制器进入到急停状态")
                    else:
                        self.states.cutterZmcStates.rs = False
                        if self.states.cutterZmcIsRs:
                            self.states.cutterZmcIsRs = False
                            self.logger.debug("检测到铡刀控制器急停状态去除!")

                ret, val = self.cutterZmc.is_running()
                if ret == 0:
                    if val == 1:
                        self.states.cutterZmcStates.run = True
                        if not self.states.cutterZmcIsRun:
                            self.states.cutterZmcIsRun = True
                            self.logger.debug("检测到铡刀进入运行状态")
                    else:
                        self.states.cutterZmcStates.run = False
                        if self.states.cutterZmcIsRun:
                            self.states.cutterZmcIsRun = False
                            self.logger.debug("检测到铡刀进入停止状态")
            else:
                connectRes = self.cutterZmc.reconnect_cutter_zmc()
                if connectRes == "ok":
                    self.states.cutterZmcStates.online = True
                    self.states.cutterZmcStates.rs = False
                    self.states.cutterZmcStates.run = False
                else:
                    self.states.cutterZmcStates.online = False
                    self.states.cutterZmcStates.rs = False
                    self.states.cutterZmcStates.run = False
            gevent.sleep(self.loopDuration)

    def get_offline_alarm_info_by_bit_val(self, val):
        """
        根据底层的bit位信息,获取报警信息,如果没有报警信息,就返回空串,或者出错了,也返回空串
        :param val:
        :return:
        """
        try:
            alarmVal = [500 + index for index, bitVal in enumerate(val) if bitVal != 0]
            # 获取报警信息的内容
            res, alarmData = self.db.get_offline_alarm_info()
            if res != "ok":
                errorInfo = "in get_offline_alarm_info_by_bit_val() error: get_offline_alarm_info 数据错误,数据库异常"
                print(errorInfo)
                self.logger.debug(errorInfo)
            else:
                infoDict = alarmData.get("offlineAlarmInfo")
                alarmInfo = ""
                for alarmBit in alarmVal:
                    info = infoDict.get(str(alarmBit), "")
                    if info != "":
                        if alarmInfo != "":
                            alarmInfo = alarmInfo + ", " + info
                        else:
                            alarmInfo = info
                return alarmInfo
        except Exception as e:
            errorInfo = "in get_offline_alarm_info_by_bit_val: error:{}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    def get_main_alarm_info_by_bit_val(self, val):
        """
        根据底层的bit位信息,获取报警信息,如果没有报警信息,就返回空串,或者出错,也返回空串
        :param val:
        :return:
        """
        try:
            alarmVal = [500 + index for index, bitVal in enumerate(val) if bitVal != 0]
            # 获取报警信息的内容
            res, alarmData = self.db.get_main_alarm_info()
            if res != "ok":
                errorInfo = "in get_alarm_info_by_bit_val() error: get_main_alarm_info数据错误,数据库异常"
                print(errorInfo)
                self.logger.debug(errorInfo)
            else:
                infoDict = alarmData.get("mainAlarmInfo")
                alarmInfo = ""
                for alarmBit in alarmVal:
                    info = infoDict.get(str(alarmBit), "")
                    if info != "":
                        if alarmInfo != "":
                            alarmInfo = alarmInfo + ", " + info
                        else:
                            alarmInfo = info
                return alarmInfo
        except Exception as e:
            errorInfo = "in get_alarm_info_by_bit_val: error:{}".format(str(e))
            print(errorInfo)
            self.logger.debug(errorInfo)

    def camera_group_listener(self):
        """
        相机组的监听线程类
        :return:
        """
        leftCamConnected = True
        rightCamConnected = True
        cutterCam1Connected = True
        cutterCam2Connected = True
        while settings.IS_MAIN_ALIVE:
            leftCamIsOnline = self.leftCam.is_device_online()
            rightCamIsOnline = self.rightCam.is_device_online()
            cutterCam1IsOnline = self.cutterCam1.is_device_online()
            cutterCam2IsOnline = self.cutterCam2.is_device_online()
            self.states.cameraGroupStates = [leftCamIsOnline, rightCamIsOnline, cutterCam1IsOnline, cutterCam2IsOnline]
            if not leftCamIsOnline:
                ret = self.reconnect_left_cam()
                if ret != 0:
                    if leftCamConnected:
                        self.logger.debug(ret)
                    leftCamConnected = False
                else:
                    leftCamIsOnline = True
                    leftCamConnected = True
                    self.logger.debug("上料左相机连接成功!")

            if not rightCamIsOnline:
                ret = self.reconnect_right_cam()
                if ret != 0:
                    if rightCamConnected:
                        self.logger.debug(ret)
                    rightCamConnected = False
                else:
                    rightCamIsOnline = True
                    rightCamConnected = True
                    self.logger.debug("上料右相机连接成功!")

            if not cutterCam1IsOnline:
                ret = self.reconnect_cutter_cam1()
                if ret != 0:
                    if cutterCam1Connected:
                        self.logger.debug(ret)
                    cutterCam1Connected = False
                else:
                    cutterCam1IsOnline = True
                    cutterCam1Connected = True
                    self.logger.debug("铡刀1号相机连接成功!")

            if not cutterCam2IsOnline:
                ret = self.reconnect_cutter_cam2()
                if ret != 0:
                    if cutterCam2Connected:
                        self.logger.debug(ret)
                    cutterCam2Connected = False
                else:
                    cutterCam2IsOnline = True
                    cutterCam2Connected = True
                    self.logger.debug("铡刀2号相机连接成功!")

            self.states.cameraGroupStates = [leftCamIsOnline, rightCamIsOnline, cutterCam1IsOnline, cutterCam2IsOnline]
            gevent.sleep(self.loopDuration)

    def manager(self):
        """
        线程管理类
        :return:
        """
        self.updateStatesFlag = False
        while settings.IS_MAIN_ALIVE:
            if not self.states.camZmcStates.run and not self.states.cbjZmcStates.run and not self.states.cutterZmcStates.run:
                self.states.workStopFlag = True
            # 实时更新设备状态
            self.send_device_state_to_ui()
            # 将设备状态写入到数据库,好发送给eap
            self.write_device_state_to_mogodb()
            try:
                if not self.offlineZmcListenerThread.is_alive():
                    self.offlineZmcListenerThread = threading.Thread(target=self.offline_zmc_listener, name="offlineZmcListenerThread")
                    self.offlineZmcListenerThread.setDaemon(True)
                    self.offlineZmcListenerThread.start()

                if not self.camZmcListenerThread.is_alive():
                    self.camZmcListenerThread = threading.Thread(target=self.cam_zmc_listener, name="camZmcListenerThread")
                    self.camZmcListenerThread.setDaemon(True)
                    self.camZmcListenerThread.start()

                if not self.cbjZmcListenerThread.is_alive():
                    self.cbjZmcListenerThread = threading.Thread(target=self.cbj_zmc_listener, name="cbjZmcListenerThread")
                    self.cbjZmcListenerThread.setDaemon(True)
                    self.cbjZmcListenerThread.start()

                if not self.cutterZmcListenerThread.is_alive():
                    self.cutterZmcListenerThread = threading.Thread(target=self.cutter_zmc_listener, name="cutterZmcListenerThread")
                    self.cutterZmcListenerThread.setDaemon(True)
                    self.cutterZmcListenerThread.start()

                if not self.cameraGroupListenerThread.is_alive():
                    self.cameraGroupListenerThread = threading.Thread(target=self.camera_group_listener, name="cameraGroupListenerThread")
                    self.cameraGroupListenerThread.setDaemon(True)
                    self.cameraGroupListenerThread.start()

                if not self.updateStatesFlag:
                    loginSuccess = self.db.get_login_state()
                    if loginSuccess:
                        gevent.sleep(0.3)
                        self.web_show_data_initial()
                        self.db.update_login_state(False)
                        self.updateStatesFlag = True
            except Exception as e:
                errorInfo = "in manager() error: {}".format(str(e))
                print(errorInfo)
                self.logger.debug(errorInfo)
                gevent.sleep(1)

            gevent.sleep(1)

    def web_show_data_initial(self):
        """
        前端界面的一些显示数据的初始化
        :return:
        """
        orderUsedKey = self.db.get_working_used_key()
        msg = {
            "val": orderUsedKey
        }
        send_order_state_to_ui(msg)
        # 更新workingInfo的数据
        send_working_info_state_to_ui()
        # 更新设置界面的参数
        send_settings_update_to_ui()
        # 更新裁切机控制器工具中的界面的参数
        msgValue = {
            "val": "updateKnifeCutParams",
        }
        send_cbj_zmc_tool_state_to_ui(msgValue)
        # 更新相机拼接参数到相机拼接的界面
        msgValue = {
            "val": "updateJointParams",
        }
        send_line_camera_joint_state(msgValue)
        # 更新铡刀标定的时候的参数信息到界面
        msgValue = {
            "val": "updateLightParams",
        }
        send_cutter_camera_cal_state(msgValue)
        # msgValue = {
        #     "val": "updateEapParamsState",
        # }
        # send_eap_msg_state_to_ui(msgValue)

    def send_device_state_to_ui(self):
        """
        将device的状态信息发送给前端,就是状态类监控到的设备信息
        :return:
        """
        msgType = "deviceStates"
        msg = self.states.pack_state_to_dict()
        send_msg_to_ui(msgType, msg)

    def write_device_state_to_mogodb(self):
        """
        将设备的状态写入到数据库中
        :return:
        """
        camZmcRsVal = "0"  # 默认是0,如果有急停,就更改它的值
        cbjZmcRsVal = "0"  # 默认是0,如果有急停,就更改它的值
        cutterZmcRsVal = "0"  # 默认是0,如果有急停,就更改它的值

        if self.states.camZmcStates.rs:
            camZmcStatesVal = "2"
            camZmcRsVal = "上料控制器急停"
        elif self.states.camZmcStates.run:
            camZmcStatesVal = "1"
        else:
            camZmcStatesVal = "0"

        if self.states.cbjZmcStates.rs:
            cbjZmcStatesVal = "2"
            cbjZmcRsVal = "裁切机控制器急停"
        elif self.states.cbjZmcStates.run:
            cbjZmcStatesVal = "1"
        else:
            cbjZmcStatesVal = "0"

        if self.states.cutterZmcStates.rs:
            cutterZmcStatesVal = "2"
            cutterZmcRsVal = "铡刀控制器急停"
        elif self.states.cutterZmcStates.run:
            cutterZmcStatesVal = "1"
        else:
            cutterZmcStatesVal = "0"

        query = {
            "key": "1"
        }
        newDeviceStatesVal = {
            "$set": {
                "camZmcStates": camZmcStatesVal,
                "cbjZmcStates": cbjZmcStatesVal,
                "cutterZmcStates": cutterZmcStatesVal
            }
        }
        self.db.update_data(self.db.deviceStateToEapTable, query, newDeviceStatesVal)

        newAlarmReportVal = {
            "$set": {
                "camZmcRs": camZmcRsVal,
                "cbjZmcRs": cbjZmcRsVal,
                "cutterZmcRs": cutterZmcRsVal
            }
        }
        self.db.update_data(self.db.alarmReportToEapTable, query, newAlarmReportVal)

    def reconnect_left_cam(self):
        """
        重新连接上料左相机
        :return: 成功返回0,失败返回失败原因
        """
        try:
            ret = self.leftCam.re_connect_line_camera()
            if ret != 0:
                self.leftCam.errorMsg = "上料左相机连接失败,原因:{}".format(ret)
                return self.leftCam.errorMsg
            else:
                self.leftCam.start_grabbing()  # 打开相机
                return 0
        except Exception as e:
            print("in reconnect_left_cam() error : {}".format(str(e)))
            return "上料左相机连接失败,失败原因:{}".format(str(e))

    def reconnect_right_cam(self):
        """
        重新连接右相机
        :return: 成功返回0,失败返回失败原因
        """
        try:
            ret = self.rightCam.re_connect_line_camera()
            if ret != 0:
                self.rightCam.errorMsg = "上料右相机连接失败,原因:{}".format(ret)
                return self.rightCam.errorMsg
            else:
                self.rightCam.start_grabbing()  # 打开相机
                return 0
        except Exception as e:
            print("in reconnect_right_cam() error : {}".format(str(e)))
            return "上料右相机连接失败,失败原因:{}".format(str(e))

    def reconnect_cutter_cam1(self):
        """
        重新连接铡刀相机
        :return: 成功返回0 失败返回失败原因
        """
        try:
            ret = self.cutterCam1.re_connect_area_camera()
            if ret != 0:
                self.cutterCam1.errorMsg = "铡刀1号相机连接失败,原因:{}".format(ret)
                return self.cutterCam1.errorMsg
            else:
                return 0
        except Exception as e:
            print("in reconnect_cutter_cam1() error : {}".format(str(e)))
            return "铡刀1号相机连接失败,失败原因:{}".format(str(e))

    def reconnect_cutter_cam2(self):
        """
        重新连接铡刀2号相机
        :return: 成功返回0,失败返回失败原因
        """
        try:
            ret = self.cutterCam2.re_connect_area_camera()
            if ret != 0:
                self.cutterCam2.errorMsg = "铡刀2号相机连接失败,原因:{}".format(ret)
                return self.cutterCam2.errorMsg
            else:
                return 0
        except Exception as e:
            print("in reconnect_cutter_cam2() error : {}".format(str(e)))
            return "铡刀2号相机连接失败,失败原因:{}".format(str(e))
